#include <assert.h>
#include <allegro.h>
#include "game.h"
#include "main.h"
#include "map.h"
#include "network.h"
#include "logic.h"
#include "dungeon.h"
#include "bailout.h"
#include "mt.h"

#define MAX_ENEMIES

static Map* map = NULL;
int displayed_player = 0;
StoCmessage netmessage; // For communications to clients
CtoSmessage received;  // For communications to clients
unsigned long seed;

void game_damage_handler (int time, coord_t where, int attacker, int victim, int weapon, int damage);

void game_init()
{	
	for (int i = 0; i < MAX_PLAYERS; i++) {
		player_init (&player[i]);
		memset (&player[i].pos, 0, sizeof(player[i].pos));
		memset (&player[i].action, 0, sizeof(player[i].action));
		memset (&player[i].want_action, 0, sizeof(player[i].want_action));
		player[i].pos.x = 2 + i*2;
		player[i].pos.y = 7;
		player[i].pos.z = 0;
		
		#if TOWERHACK_DEBUG
		int item = mt_randint (WEAPONS_CLOSE_BEGIN, WEAPONS_CLOSE_END);
		player[i].inv.slot[items[item].type - ITEM_WEAPON_CLOSE] = item;
		#endif
	}	
	
	if (map == NULL) {
		map = malloc(sizeof(Map));
		map_init (map, MAP_WIDTH, MAP_HEIGHT);
	}

	logic_reset();

	damage_handler = &game_damage_handler; // Set handler for logic
}

// Creates a map, maybe?
void game_seed(unsigned long s)
{
	seed = s;
	
	if (map == NULL) {
		map = malloc(sizeof(Map));
		map_init (map, MAP_WIDTH, MAP_HEIGHT);
	}
	generate_amazing_maze (map, s, NUM_DUNGEON_SEGMENTS);
}

void game_update(void)
{
	logic_update (map);
	
	int sender;
	
	receive_CtoSmessage (&received, &sender);
	while (received.type != CtoS_NOMSG) {
		switch (received.type) {
			
		case CtoS_INGAME_ACTION:
			logic_enqueue (&received.action);
			netmessage.type = StoC_INGAME_ACTION;
			netmessage.action = received.action;
			send_StoCmessage (&netmessage, -1);
			break;
			
		case CtoS_INGAME_SETINVENTORY:
			assert ((received.player >= 0) && (received.player < MAX_PLAYERS));
			if (owner[received.player] != client_by_uid (received.uid)) break; // TODO: kick client if he doesn't own the player
			memcpy (&player[received.player].inv, &received.inv, sizeof(Inventory));
			netmessage.type = StoC_INGAME_SETPLAYER;
			netmessage.who = received.player;
			netmessage.level = player[received.player].skill.level;
			netmessage.hp = player[received.player].skill.max_hp;
			memcpy (&netmessage.inv, &player[received.player].inv, sizeof(Inventory));
			send_StoCmessage (&netmessage, -1);
			break;

		case CtoS_CHAT:
			netmessage.type = StoC_CHAT;
			strcpy (netmessage.line, received.line);
			send_StoCmessage (&netmessage, -1);
			break;
			
		default:
			bailout ("sds", "Received unidentified message type ", received.type, " from client.");
			break;
			
		}
		
		receive_CtoSmessage (&received, &sender);
	}
	
	#if TOWERHACK_DEBUG
	if ((game_time % 300) == 299) {
		netmessage.type = StoC_INGAME_DAMAGE;
		netmessage.damage.time = game_time+20;
		netmessage.damage.where = (coord_t) {7,7,0};
		netmessage.damage.attacker = 0;
		netmessage.damage.victim = 0;
		netmessage.damage.weapon = 0;
		netmessage.damage.damage = 10;
		netmessage.damage.hp = 50;
		send_StoCmessage (&netmessage, -1);
	}
	#endif
}

void game_cleanup(void)
{
	if (map != NULL) {
		free(map);
		map = NULL;
	}
}

void game_damage_handler (int time, coord_t where, int attacker, int victim, int weapon, int damage)
{
	#if TOWERHACK_DEBUG
	fprintf (stderr, "Server: deal %d damage from %d to %d.\n", damage, attacker, victim);
	#endif
	
	int hp = 0;

	if (actor_type (victim) == ACTOR_PLAYER) {
		Player* p = (Player*) player_from_actor_id (victim);
		p->hp -= damage;
		hp = p->hp;
	}
		
	netmessage.type = StoC_INGAME_DAMAGE;
	netmessage.damage.time     = time;
	netmessage.damage.where    = where;
	netmessage.damage.attacker = attacker;
	netmessage.damage.victim   = victim;
	netmessage.damage.weapon   = weapon;
	netmessage.damage.damage   = damage;
	netmessage.damage.hp       = hp;
	send_StoCmessage (&netmessage, -1);
	
	if (hp <= 0) {
		netmessage.type = StoC_INGAME_DIE;
		send_StoCmessage (&netmessage, -1);
	}
}
